package cn.liujianbo.example.validate.config;

import cn.liujianbo.example.validate.result.R;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author 刘建波
 * @date 2020-12-25 09:48
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 参数封装时类型不匹配
     * <pre>
     *     @DeleteMapping("{id}")
     *     R del(@PathVariable Long id){return R.ok();}
     * </pre>
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public R methodArgumentTypeMismatch(MethodArgumentTypeMismatchException e) {
        return R.error(e.getMessage());
    }

    /**
     * @Validate 或者 @Valid 标注的实体校验失败  非 @RequestBody 修饰
     * <pre>
     *     @PostMapping
     *      *     R add(@Validated(ValidateGroup.Add.class) E e);
     * </pre>
     * @param result
     * @return
     */
    @ExceptionHandler(BindException.class)
    public R bindExceptionHandle(BindingResult result) {
        R<Object> r = R.error("参数未通过验证");

        if (result.hasErrors()) {
            List<ObjectError> errors = result.getAllErrors();
            HashMap<String, String> errorMap = new HashMap<>();

            errors.forEach(p -> {

                FieldError fieldError = (FieldError) p;
                errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
            });
            r.setResult(errorMap);
        }

        return r;
    }

    /**
     * @Validate 或者 @Valid 标注的实体校验失败
     * <pre>
     *     @PostMapping
     *     R add(@Validated(ValidateGroup.Add.class) @RequestBody E e);
     * </pre>
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R methodArgumentNotValidHandler(MethodArgumentNotValidException e) {
        R<Object> r = R.error("参数校验不通过");
        BindingResult result = e.getBindingResult();
        if (result != null && result.hasErrors()) {
            List<ObjectError> errors = result.getAllErrors();
            HashMap<String, String> errorMap = new HashMap<>(errors.size() * 2);

            errors.forEach(p -> {

                FieldError fieldError = (FieldError) p;
                errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
            });
            r.setResult(errorMap);
        }
        return r;
    }
    /**
     * controller方法中直接标明的校验注解校验失败
     *<pre>
     *     @PostMapping("addAll")
     *     public R addBatch(@Size(min = 1, message = "用户信息未传递") @RequestBody List<User> list) {
     *         return R.ok();
     *     }
     *</pre>
     * @param e
     * @return com.flyduck.cms.vo.ApiResult
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public R constraintViolationHandler(ConstraintViolationException e) {
        R r = R.error("参数未通过验证");
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        if (violations.size() > 0) {
            Map<String, String> errorMap = new HashMap<>();
            for (ConstraintViolation<?> violation : violations) {
                String fieldName = null;
                Iterator<Path.Node> iterator = violation.getPropertyPath().iterator();
                while (iterator.hasNext()) {
                    fieldName = iterator.next().getName();
                }
                if (!StringUtils.isEmpty(fieldName)) {
                    errorMap.put(fieldName, violation.getMessage());
                }
            }
            r.setResult(errorMap);
        }
        return r;
    }
}
